package org.brownflat.lighthouse.xml;

import java.io.ByteArrayInputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;
import org.jaxen.JaxenException;
import org.jaxen.SimpleNamespaceContext;
import org.jaxen.XPath;
import org.jaxen.dom4j.Dom4jXPath;
import org.xml.sax.InputSource;


public class XMLDocument {
	private Document document;
	private Map namespaceContexts = Collections.EMPTY_MAP;

	public XMLDocument(String xml) {
		init(xml);
	}
	
	public XMLDocument(Node node) {
		init(node.asXML());
	}
	
	private void init(String xml) {
		SAXReader reader = new SAXReader();
		try
		{
			byte[] bytes = xml.getBytes();
			ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
			InputSource input = new InputSource();
			input.setByteStream(byteArrayInputStream);
			document = reader.read(input);
		}
		catch(DocumentException de) {
			throw new XMLDocumentException(de);
		}
	}
	
	public XMLDocument documentAt(String path) {
		try {
			XMLDocument newDocument = new XMLDocument((Node)getXPath(path).selectSingleNode(this.document));
			newDocument.setNamespaceContexts(namespaceContexts);
			return newDocument;
		} catch (JaxenException e) {
			throw new RuntimeException(e);
		}		
	}
	
	public List<XMLDocument> documentsAt(String path) {
		List<XMLDocument> documents = new ArrayList<XMLDocument>();
		XPath xpath = getXPath(path);
		try {
			List nodes = xpath.selectNodes(this.document);
			for (Iterator iterator = nodes.iterator(); iterator.hasNext();) {
				Node node = (Node) iterator.next();
				XMLDocument newDocument = new XMLDocument(node);
				newDocument.setNamespaceContexts(namespaceContexts);
				documents.add(newDocument);			
			}
			return documents;		
		} catch (JaxenException e) {
			throw new RuntimeException(e);
		}
	}
	
	public String selectValueAt(String path) {
		Node result = selectNode(path);
		if (result == null)	{
			return "";
		}
		return result.getText();
	}

	public String toXML() {
		return document.asXML();
	}
	
	public String toString() {
		return toXML();
	}
	
	public void setNamespaceContexts(Map<String, String> namespaceContexts) {
		this.namespaceContexts = namespaceContexts;
	}
	
	private XPath getXPath(String path) {
		  
		XPath xpath = null;
		try {
			xpath = new Dom4jXPath(path);
		} catch (JaxenException e) {
			throw new RuntimeException(e);
		}
		xpath.setNamespaceContext(new SimpleNamespaceContext(namespaceContexts));
		return xpath;
	}
	
	private Node selectNode(String path) {
		try {
			return (Node) getXPath(path).selectSingleNode(this.document);
		} catch (JaxenException e) {
			throw new RuntimeException(e);
		}
	}
}
